//=============================================================================
// Mesh Viewer.cpp by Frank Luna (C) 2008 All Rights Reserved.
//
// Demonstrates the ID3DX10Mesh class. 
//
// Controls:
//		'A'/'D'/'W'/'S' - Move
//      Hold down left mouse button and move mouse to rotate.
//
//=============================================================================

//#include "Physics.h"
#include "Game Engine/Header/Framework.h"
#include "Game Engine/Header/Light.h"
#include "Game Engine/Header/Camera.h"
#include "Source/Header/Effects.h"
#include "Source/Header/InputLayouts.h"
#include "Game Engine/Header/TextureMgr.h"
#include "Source/Header/Sky.h"
#include "Source/Header/DrawableTex2D.h"
#include "Game Engine/Header/Mesh.h"
#include "Game Engine/Header/Player.h"
#include "Game Engine/Header/Object.h"
#include "Game Engine/Header/Environment.h"
#include "Game Engine/Header/Static.h"

#define NUM_BOXES 2
#define NUM_ENV 5
#define NUM_STATIC 2

#define NUM_OBJECTS NUM_BOXES + NUM_ENV + NUM_STATIC

class MeshViewerApp : public Framework
{
public:
	MeshViewerApp(HINSTANCE hInstance);
	~MeshViewerApp();

	void InitApp();
	void OnResize();
	void UpdateScene(float dt);
	void DrawScene(); 

	LRESULT MsgProc(UINT msg, WPARAM wParam, LPARAM lParam);
 
private:
	POINT mOldMousePos;

	Mesh mBaseMesh;
	Mesh mSphereMesh;
	Mesh mPlaneMesh;
	Mesh mWallMesh;

	Player* mPlayer[NUM_BOXES];

	Environment* mEnv[NUM_ENV];

	Static* mStatic[NUM_STATIC];

	Sky mSky;

	//Physics physics;

	DrawableTex2D mShadowMap;

	Light mParallelLight;

	D3DXMATRIX mBaseWorld;
	D3DXMATRIX mPlaneWorld;
	D3DXMATRIX mWallWorld;

	D3DXMATRIX mLightView;
	D3DXMATRIX mLightVolume;

	ID3D10ShaderResourceView* mEnvMapRV;

	ID3D10EffectTechnique* mBuildShadowMapTech;
	ID3D10EffectMatrixVariable* mfxBuildShadowMapLightWVPVar;
	ID3D10EffectShaderResourceVariable* mfxBuildShadowMapDiffuseMapVar;
	ID3D10EffectMatrixVariable* mfxTexMtxVar;
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
				   PSTR cmdLine, int showCmd)
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif


	MeshViewerApp theApp(hInstance);
	
	theApp.InitApp();

	return theApp.Run();
}

MeshViewerApp::MeshViewerApp(HINSTANCE hInstance)
: Framework(hInstance), mEnvMapRV(0)
{
	D3DXMatrixIdentity(&mBaseWorld);
	D3DXMatrixIdentity(&mPlaneWorld);
	D3DXMatrixIdentity(&mWallWorld);
}

MeshViewerApp::~MeshViewerApp()
{
	if( md3dDevice )
		md3dDevice->ClearState();

	fx::DestroyAll();
	InputLayout::DestroyAll();
}

void MeshViewerApp::InitApp()
{
	Framework::InitApp();

	fx::InitAll(md3dDevice);
	InputLayout::InitAll(md3dDevice);
	GetTextureMgr().init(md3dDevice);

	mClearColor = D3DXCOLOR(0.4f, 0.4f, 0.4f, 1.0f);

	mBuildShadowMapTech            = fx::BuildShadowMapFX->GetTechniqueByName("BuildShadowMapTech");
	mfxBuildShadowMapLightWVPVar   = fx::BuildShadowMapFX->GetVariableByName("gLightWVP")->AsMatrix();
	mfxBuildShadowMapDiffuseMapVar = fx::BuildShadowMapFX->GetVariableByName("gDiffuseMap")->AsShaderResource();
	mfxTexMtxVar				   = fx::MeshFX->GetVariableByName("gTexMtx")->AsMatrix();
	
	mShadowMap.init(md3dDevice, 1024, 1024, false, DXGI_FORMAT_UNKNOWN);

	//mPlayer = new Player();

	//mPlayer->Position() = D3DXVECTOR3(0.0f, 1.8f, -10.0f);
	GetCamera().Position() = D3DXVECTOR3(0.0f, 1.8f, -10.0f);//mPlayer->Position();
 
	mEnvMapRV = GetTextureMgr().createCubeTex(L"Textures/grassenvmap1024.dds");
	mSky.init(md3dDevice, mEnvMapRV, 5000.0f);

	mBaseMesh.Init(md3dDevice, L"M3D/Cube2.m3d", fx::MeshFX, false);
	mBaseMesh.SetCubeMap(mEnvMapRV);

	mSphereMesh.Init(md3dDevice, L"M3D/Sphere.m3d", fx::MeshFX, false);
	mSphereMesh.SetCubeMap(mEnvMapRV);

	mPlaneMesh.Init(md3dDevice, L"M3D/Plane.m3d", fx::MeshFX, false);
	mPlaneMesh.SetCubeMap(mEnvMapRV);

	mWallMesh.Init(md3dDevice, L"M3D/Wall.m3d", fx::MeshFX, false);
	mWallMesh.SetCubeMap(mEnvMapRV);

	//physics.Init( mDynamicsWorld, D3DXVECTOR3( 0.0f, 5.0f, 0.0f ), D3DXVECTOR3( 1.0f, 1.0f, 1.0f ) );

	for( UINT i = 0; i < NUM_BOXES; i ++ )
	{
		mPlayer[i] = new Player();
		mPlayer[i]->Init( &mBaseMesh, Object::Cube );
		mPlayer[i]->SetPosition( D3DXVECTOR3( i * 2.0f, 5.0f, 5.0f ) );
		Framework::mBP->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
		mDynamicsWorld->addCollisionObject(mPlayer[i]->mGhostObject,btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::AllFilter);// btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);

		mDynamicsWorld->addAction(mPlayer[i]->mCharacter);

		//mPlayer[i]->mCharacter->reset();

		//mDynamicsWorld->addRigidBody( mPlayer[i]->mBody );
	}

	UINT j = 0;
	UINT k = 0;
	for( UINT i = 0; i < NUM_ENV; i ++ )
	{
		mEnv[i] = new Environment();
		if( j / 5.0f == 1.0f )
		{
			k++;
			j = 0;
		}
		mEnv[i]->Init( &mSphereMesh, Object::Sph );
		mEnv[i]->SetPosition( D3DXVECTOR3( j * 1.0f, 5.0f + k * 1.0f, 0.0f ) );
		mDynamicsWorld->addRigidBody( mEnv[i]->mBody );
		j++;
	}

	mStatic[0] = new Static();
	mStatic[0]->Init( &mPlaneMesh, Object::Cube, false );
	mStatic[0]->SetPosition( D3DXVECTOR3( 0.0f, -0.5f, 0.0f ) );
	mDynamicsWorld->addRigidBody( mStatic[0]->mBody );

	mStatic[1] = new Static();
	mStatic[1]->Init( &mWallMesh, Object::Cube, false );
	mStatic[1]->SetPosition( D3DXVECTOR3( -10.0f, 0.5f, 0.0f ) );
	mDynamicsWorld->addRigidBody( mStatic[1]->mBody );

	// direction updated at runtime
	mParallelLight.ambient  = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
	mParallelLight.diffuse  = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	mParallelLight.specular = D3DXCOLOR(0.5f, 0.4843f, 0.3f, 1.0f);

	D3DXMatrixOrthoLH(&mLightVolume, 20.0f, 20.0f, 1.0f, 100.0f);
}

void MeshViewerApp::OnResize()
{
	Framework::OnResize();

	float aspect = (float)mClientWidth/mClientHeight;
	GetCamera().SetLens(0.25f*PI, aspect, 0.5f, 1000.0f);
}

void MeshViewerApp::UpdateScene(float dt)
{
	Framework::UpdateScene(dt);

	// Update angles based on input to orbit camera around scene.
	if(GetAsyncKeyState('A') & 0x8000)	GetCamera().Strafe(-10.3f*dt);
	if(GetAsyncKeyState('D') & 0x8000)	GetCamera().Strafe(+10.3f*dt);
	if(GetAsyncKeyState('W') & 0x8000)	GetCamera().Walk(+10.3f*dt);
	if(GetAsyncKeyState('S') & 0x8000)	GetCamera().Walk(-10.3f*dt);
 
	if(GetAsyncKeyState(VK_LEFT) & 0x8000)
	{
		mPlayer[0]->Strafe( -70.0f * dt );
		mPlayer[1]->Strafe( +70.0f * dt );
	}
	if(GetAsyncKeyState(VK_RIGHT) & 0x8000)
	{
		mPlayer[0]->Strafe( +70.0f * dt );
		mPlayer[1]->Strafe( -70.0f * dt );
	}
	if(GetAsyncKeyState(VK_UP) & 0x8000)
	{
		mPlayer[0]->Walk( +70.0f * dt );
	}
	if(GetAsyncKeyState(VK_DOWN) & 0x8000)
	{
		mPlayer[0]->Walk( -70.0f * dt );
	}

	if(GetAsyncKeyState('J') & 0x8000)
		mEnv[0]->SetPosition( D3DXVECTOR3( 0.0F, 0.4F, 0.0F ) );

	//GetCamera().Position() = mPlayer.Position();
	GetCamera().RebuildView();

	mPlayer[0]->mCharacter->setWalkDirection(BTVector3( mPlayer[0]->GetVelocity() ));
	mPlayer[1]->mCharacter->setWalkDirection(BTVector3( mPlayer[1]->GetVelocity() ));
	//mBase[0].Position() += mBase[0].Velocity();

	//physics.Update( dt );

	// Animate light and keep shadow in sync.
	D3DXVECTOR3 lightPos;
	lightPos.x = 30.0f*cosf(0.1f*mTimer.getGameTime());
	lightPos.y = 20.0f;
	lightPos.z = 30.0f*sinf(0.1f*mTimer.getGameTime());

	D3DXMatrixLookAtLH(&mLightView, &lightPos,
		&D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 1.0f, 0.0f));

	D3DXVECTOR3 lightDirection = -lightPos;
	D3DXVec3Normalize(&mParallelLight.dir, &lightDirection);
}

void MeshViewerApp::DrawScene()
{
	Framework::DrawScene();
	
	// Restore default states, input layout and primitive topology 
	// because mFont->DrawText changes them.  Note that we can 
	// restore the default states by passing null.
	md3dDevice->OMSetDepthStencilState(0, 0);
	float blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
	md3dDevice->OMSetBlendState(0, blendFactor, 0xffffffff);
	
	md3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	md3dDevice->IASetInputLayout(InputLayout::PosTangentNormalTex);


	//////////////////// Uncomment this section to use shadows /////////////////////
	//////// Make sure to set useShadows to true in the mesh.Init function /////////

	////
	//// Draw scene to the shadow map.
	////

	//mShadowMap.begin();

	//D3D10_TECHNIQUE_DESC techDesc;
	//mBuildShadowMapTech->GetDesc( &techDesc );

	//for(UINT i = 0; i < techDesc.Passes; ++i)
	//{
	//	ID3D10EffectPass* pass = mBuildShadowMapTech->GetPassByIndex(i);

	//	D3DXMATRIX WVP;

	//	WVP = mBaseWorld*mLightView*mLightVolume;
	//	mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
	//	mBaseMesh.DrawToShadowMap(mfxBuildShadowMapDiffuseMapVar, pass);
	//}

	//mShadowMap.end();

	//// restore rendering to backbuffer
	//ResetOMTargetsAndViewport();

	//mBaseMesh.SetShadowMap( mShadowMap.depthMap() );

	////////////////////////////////////////////////////////////////////////////////

	mBaseMesh.SetLight( mParallelLight );
	mBaseMesh.SetEyePos( GetCamera().Position() );
	mBaseMesh.EnableCubeMap( false );


	const int	numObjects=mDynamicsWorld->getNumCollisionObjects();
	btVector3 wireColor(1,0,0);
	btTransform	rot;rot.setIdentity();
	btScalar	m[16];
	D3DXMATRIX M;
	for( UINT i = 0; i < NUM_OBJECTS; i ++ )
	{
		D3DXMATRIX I;
		D3DXMatrixIdentity(&I);
		mfxTexMtxVar->SetMatrix((float*)&I);
		btCollisionObject*	colObj=mDynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody*		body=btRigidBody::upcast(colObj);
		if(body&&body->getMotionState())
		{
			btDefaultMotionState* myMotionState = (btDefaultMotionState*)body->getMotionState();
			myMotionState->m_graphicsWorldTrans.getOpenGLMatrix(m);
			rot=myMotionState->m_graphicsWorldTrans;
			if( i < NUM_BOXES + NUM_ENV )
			{
				mEnv[i - NUM_BOXES]->Draw( mLightView * mLightVolume, M );
			}
			else
			{
				D3DXMATRIX S;
				D3DXMatrixScaling(&S, 10.0f, 10.0f, 1.0f);
				D3DXMATRIX landTexMtx = S;
				mfxTexMtxVar->SetMatrix((float*)&landTexMtx);
				mStatic[i - NUM_ENV - NUM_BOXES]->Draw( mLightView * mLightVolume, M );
			}
		}
		else
		{
			M = BT2DX_MATRIX(colObj->getWorldTransform());
			mPlayer[i]->Draw( mLightView * mLightVolume, M );
			//colObj->getWorldTransform().getOpenGLMatrix(m);
			//rot=colObj->getWorldTransform();
		}
	}
	//mPlaneMesh.Draw( mStatic[0]->World(), mLightView * mLightVolume );
	//mWallMesh.Draw( mStatic[1]->World(), mLightView * mLightVolume );

	//for( UINT i = 0; i < NUM_ENV; i ++ )
	//{
	//	mEnv[i]->Draw( mBaseMesh, mLightView * mLightVolume, I );
	//}
	//mBaseMesh.Draw( mBase[i]->World(), mLightView * mLightVolume );
	//physics.Render( mBaseMesh, mLightView * mLightVolume, i );

	/////////////////////// Uncomment this when using shadows //////////////////////

	//mBaseMesh.UnbindShadowMap();

	////////////////////////////////////////////////////////////////////////////////

	// Draw sky last to save fill rate.
	mSky.draw();

	// We specify DT_NOCLIP, so we do not care about width/height of the rect.
	RECT R = {5, 5, 0, 0};
	md3dDevice->RSSetState(0);
	mFont->DrawText(0, mFrameStats.c_str(), -1, &R, DT_NOCLIP, WHITE);

	mSwapChain->Present(0, 0);
}

LRESULT MeshViewerApp::MsgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	POINT mousePos;
	int dx = 0;
	int dy = 0;
	switch(msg)
	{
	case WM_LBUTTONDOWN:
		if( wParam & MK_LBUTTON )
		{
			SetCapture(mMainWnd);

			mOldMousePos.x = LOWORD(lParam);
			mOldMousePos.y = HIWORD(lParam);
		}
		return 0;

	case WM_LBUTTONUP:
		ReleaseCapture();
		return 0;

	case WM_MOUSEMOVE:
		if( wParam & MK_LBUTTON )
		{
			mousePos.x = (int)LOWORD(lParam); 
			mousePos.y = (int)HIWORD(lParam); 

			dx = mousePos.x - mOldMousePos.x;
			dy = mousePos.y - mOldMousePos.y;

			GetCamera().Pitch( dy * 0.0087266f );
			GetCamera().RotateY( dx * 0.0087266f );
			
			mOldMousePos = mousePos;
		}
		return 0;
	}

	return Framework::MsgProc(msg, wParam, lParam);
}
 